package com.xyzwps.server.paimon.it

import com.xyzwps.server.paimon.common.CursorPage
import com.xyzwps.server.paimon.ui.HttpResponse
import com.xyzwps.server.paimon.ui.view.ArticleView
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.web.server.LocalServerPort
import org.springframework.core.ParameterizedTypeReference
import org.springframework.http.MediaType
import org.springframework.http.ProblemDetail

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class MvcArticleTests(@LocalServerPort port: Int) : MvcTestBase(port) {

    private class EnvUid {
        var uid: String? = null
    }

    private class EnvCursor {
        var cursor: String? = null
    }

    @Test
    fun createArticleWithoutTokenTest() {
        webClient.post().uri("/apis/articles")
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(mapOf("title" to "t", "content" to "c"))
            .exchange()
            .expectStatus().isUnauthorized
            .expectBody(ProblemDetail::class.java)
            .value { p ->
                assertEquals("/errors/100", p.type.toString())
                assertEquals("UNAUTHORIZED", p.title)
                assertEquals(401, p.status)
            }
    }

    private fun createArticle(token: String, title: String, content: String): String {
        val env = EnvUid()
        webClient.post().uri("/apis/articles")
            .contentType(MediaType.APPLICATION_JSON)
            .header("Authorization", "Bearer $token")
            .bodyValue(mapOf("title" to title, "content" to content))
            .exchange()
            .expectStatus().isOk
            .expectBody(object : ParameterizedTypeReference<HttpResponse<String>>() {})
            .value { resp ->
                val uid = resp.data
                assertNotNull(uid)
                env.uid = uid
            }
        return env.uid!!
    }

    @Test
    fun updateArticleWithoutTokenTest() {
        webClient.put().uri("/apis/articles/xx.kt")
            .contentType(MediaType.APPLICATION_JSON)
            .bodyValue(mapOf("title" to "t", "content" to "c"))
            .exchange()
            .expectStatus().isUnauthorized
            .expectBody(ProblemDetail::class.java)
            .value { p ->
                assertEquals("/errors/100", p.type.toString())
                assertEquals("UNAUTHORIZED", p.title)
                assertEquals(401, p.status)
            }
    }

    private fun updateArticle(token: String, uid: String, title: String, content: String) {
        webClient.put().uri("/apis/articles/$uid")
            .contentType(MediaType.APPLICATION_JSON)
            .header("Authorization", "Bearer $token")
            .bodyValue(mapOf("title" to title, "content" to content))
            .exchange()
            .expectStatus().isOk
            .expectBody(object : ParameterizedTypeReference<HttpResponse<String>>() {})
            .value { resp ->
                val returnUid = resp.data
                assertEquals(uid, returnUid)
            }
    }

    @Test
    fun getArticleWithoutTokenTest() {
        webClient.get().uri("/apis/articles/xx.kt")
            .exchange()
            .expectStatus().isNotFound
            .expectBody(ProblemDetail::class.java)
            .value { p ->
                assertEquals("/errors/5001", p.type.toString())
                assertEquals("NOT_FOUND", p.title)
                assertEquals(404, p.status)
            }
    }

    private fun getArticle(token: String, uid: String, asserter: (ArticleView) -> Unit) {
        webClient.get().uri("/apis/articles/$uid")
            .header("Authorization", "Bearer $token")
            .exchange()
            .expectStatus().isOk
            .expectBody(object : ParameterizedTypeReference<HttpResponse<ArticleView>>() {})
            .value { resp -> asserter(resp.data) }
    }

    @Test
    fun getArticlesWithoutTokenTest() {
        webClient.get().uri("/apis/articles")
            .exchange()
            .expectStatus().isUnauthorized
            .expectBody(ProblemDetail::class.java)
            .value { p ->
                assertEquals("/errors/100", p.type.toString())
                assertEquals("UNAUTHORIZED", p.title)
                assertEquals(401, p.status)
            }
    }

    private fun getArticles(
        token: String,
        cursor: String?,
        count: Int,
        asserter: (CursorPage<ArticleView, String>) -> Unit
    ) {
        var qs = "count=$count"
        if (!cursor.isNullOrEmpty()) {
            qs += "&cursor=$cursor"
        }
        webClient.get().uri("/apis/articles?$qs")
            .header("Authorization", "Bearer $token")
            .exchange()
            .expectStatus().isOk
            .expectBody(object : ParameterizedTypeReference<HttpResponse<CursorPage<ArticleView, String>>>() {})
            .value { resp -> asserter(resp.data) }
    }

    @Test
    fun createAndGetArticleTest() {
        val (me, token) = registerAndLogin()
        val limit = 3

        val uid1 = createArticle(token, "测试文章1", "测试内容1")
        val a1Asserter: (ArticleView) -> Unit = { a ->
            assertNull(a.id)
            assertEquals(uid1, a.uid)
            assertNull(a.userId)
            assertEquals("测试文章1", a.title)
            assertEquals("测试内容1", a.content)
            assertNotNull(a.createdAt)

            assertNotNull(a.author)
            assertNull(a.author?.id)
            assertEquals(me.nickname, a.author?.nickname)
        }
        getArticle(token, uid1, a1Asserter)
        getArticles(token, null, limit) { page ->
            assertEquals(1, page.items.size)
            assertNull(page.nextCursor)
            a1Asserter(page.items.first())
        }

        val uid2 = createArticle(token, "测试文章2", "测试内容2")
        val a2Asserter: (ArticleView) -> Unit = { a ->
            assertNull(a.id)
            assertEquals(uid2, a.uid)
            assertNull(a.userId)
            assertEquals("测试文章2", a.title)
            assertEquals("测试内容2", a.content)
            assertNotNull(a.createdAt)

            assertNotNull(a.author)
            assertNull(a.author?.id)
            assertEquals(me.nickname, a.author?.nickname)
        }
        getArticle(token, uid2, a2Asserter)
        getArticles(token, null, limit) { page ->
            assertEquals(2, page.items.size)
            assertNull(page.nextCursor)

            a2Asserter(page.items.first())
            a1Asserter(page.items[1])
        }

        val uid3 = createArticle(token, "测试文章3", "测试内容3")
        val a3Asserter: (ArticleView) -> Unit = { a ->
            assertNull(a.id)
            assertEquals(uid3, a.uid)
            assertNull(a.userId)
            assertEquals("测试文章3", a.title)
            assertEquals("测试内容3", a.content)
            assertNotNull(a.createdAt)

            assertNotNull(a.author)
            assertNull(a.author?.id)
            assertEquals(me.nickname, a.author?.nickname)
        }
        getArticle(token, uid3, a3Asserter)
        getArticles(token, null, limit) { page ->
            assertEquals(3, page.items.size)
            assertNull(page.nextCursor)

            a3Asserter(page.items.first())
            a2Asserter(page.items[1])
            a1Asserter(page.items[2])
        }

        val uid4 = createArticle(token, "测试文章4", "测试内容4")
        val a4Asserter: (ArticleView) -> Unit = { a ->
            assertNull(a.id)
            assertEquals(uid4, a.uid)
            assertNull(a.userId)
            assertEquals("测试文章4", a.title)
            assertEquals("测试内容4", a.content)
            assertNotNull(a.createdAt)

            assertNotNull(a.author)
            assertNull(a.author?.id)
            assertEquals(me.nickname, a.author?.nickname)
        }
        getArticle(token, uid4, a4Asserter)
        val env = EnvCursor()
        getArticles(token, null, limit) { page ->
            assertEquals(3, page.items.size)
            assertNotNull(page.nextCursor)
            env.cursor = page.nextCursor

            a4Asserter(page.items.first())
            a3Asserter(page.items[1])
            a2Asserter(page.items[2])
        }

        getArticles(token, env.cursor, limit) { page ->
            assertEquals(1, page.items.size)
            assertNull(page.nextCursor)

            a1Asserter(page.items.first())
        }
    }

    @Test
    fun createAndUpdateArticleTest() {
        val (me, token) = registerAndLogin()
        val uid = createArticle(token, "测试文章1", "测试内容1")
        getArticle(token, uid) { a ->
            assertNull(a.id)
            assertEquals(uid, a.uid)
            assertNull(a.userId)
            assertEquals("测试文章1", a.title)
            assertEquals("测试内容1", a.content)
            assertNotNull(a.createdAt)

            assertNotNull(a.author)
            assertNull(a.author?.id)
            assertEquals(me.nickname, a.author?.nickname)
        }

        updateArticle(token, uid, "测试文章1-修改", "测试内容1-修改")
        getArticle(token, uid) { a ->
            assertNull(a.id)
            assertEquals(uid, a.uid)
            assertNull(a.userId)
            assertEquals("测试文章1-修改", a.title)
            assertEquals("测试内容1-修改", a.content)
            assertNotNull(a.createdAt)

            assertNotNull(a.author)
            assertNull(a.author?.id)
            assertEquals(me.nickname, a.author?.nickname)
        }
    }
}
